package com.wcs.exam.controller.biz;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.exception.BaseException;
import com.wcs.exam.common.constant.RedisConstant;
import com.wcs.exam.common.enums.ExamRegistrationWayEnum;
import com.wcs.exam.common.enums.GoodsTypeEnum;
import com.wcs.exam.common.enums.OrderStatusEnum;
import com.wcs.exam.common.enums.ValidityDateTypeEnum;
import com.wcs.exam.dao.ExamDao;
import com.wcs.exam.dao.ExamUserDao;
import com.wcs.exam.dao.OrderInfoDao;
import com.wcs.exam.dao.OrderPayDao;
import com.wcs.exam.dao.impl.mapper.entity.Exam;
import com.wcs.exam.dao.impl.mapper.entity.ExamUser;
import com.wcs.exam.dao.impl.mapper.entity.OrderInfo;
import com.wcs.exam.dao.impl.mapper.entity.OrderPay;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class OrderInfoCommonBiz extends BaseBiz {

    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("￥#.##");

    @NotNull
    private final RedissonClient redissonClient;

    @NotNull
    private final ExamDao examDao;
    @NotNull
    private final ExamUserDao examUserDao;

    @NotNull
    private final OrderInfoDao orderInfoDao;
    @NotNull
    private final OrderPayDao orderPayDao;

    @Transactional(rollbackFor = Exception.class)
    public void directSuccessHandle(Long userId, Long goodsId, Integer goodsType) {
        // 处理用户和商品关联
        handleOrderSuccessBiz(userId, goodsType, goodsId, "");
    }


    /**
     * 交易完成处理
     *
     * @param recordId 交易记录ID
     * @return 处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean tradeSuccessHandle(Long recordId, String payOrderNo, Date successTime) {
        // 获取交易记录并判断是否已经处理成功
        OrderPay tradeRecord = orderPayDao.getById(recordId);
        if (ObjectUtil.isNull(tradeRecord)) {
            log.error("交易记录：{}不存在", recordId);
            return false;
        }
        if (OrderStatusEnum.SUCCESS.getCode().equals(tradeRecord.getOrderStatus())) {
            log.error("交易记录：{}已经处理成功，无需重复处理", recordId);
            return true;
        }

        // 为什么针对用户上锁，因为交易完成后需要统计用户消费信息
        RLock lock = redissonClient.getLock(RedisConstant.Common.ORDER_INFO + tradeRecord.getUserId());
        try {
            boolean res = lock.tryLock(RedisConstant.DEFAULT_WAIT_TIME, RedisConstant.DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
            if (!res) {
                log.warn("处理交易记录获取锁超时");
                return false;
            }

            // 获取交易订单
            OrderInfo orderInfo = orderInfoDao.getByOrderNo(tradeRecord.getOrderNo());
            if (ObjectUtil.isNull(orderInfo)) {
                log.error("交易订单：{}不存在", orderInfo.getOrderNo());
                return false;
            }

            // 更新交易记录
            OrderPay updateOrderPay = new OrderPay();
            updateOrderPay.setId(tradeRecord.getId());
            updateOrderPay.setOrderStatus(OrderStatusEnum.SUCCESS.getCode());
            updateOrderPay.setPayTime(ObjectUtil.isNotNull(successTime) ? successTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() : LocalDateTime.now());
            orderPayDao.updateById(updateOrderPay);


            OrderInfo updateOrderInfo = new OrderInfo();
            updateOrderInfo.setId(orderInfo.getId());
            updateOrderInfo.setOrderStatus(updateOrderPay.getOrderStatus());
            updateOrderInfo.setPayTime(updateOrderPay.getPayTime());
            orderInfoDao.updateById(updateOrderInfo);

            // 处理用户和商品关联
            handleOrderSuccessBiz(orderInfo.getUserId(), orderInfo.getGoodsType(), orderInfo.getGoodsId(), orderInfo.getOrderNo());
            return true;
        } catch (InterruptedException e) {
            log.error("处理交易记录失败", e);
            Thread.currentThread().interrupt();
            return false;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 交易完成处理
     *
     * @param recordIds 交易记录ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void tradeBatchSuccessHandle(List<Long> recordIds, Date successTime) {
        for (Long recordId : recordIds) {
            tradeSuccessHandle(recordId, "", successTime);
        }
    }

    /**
     * 关闭待支付订单
     *
     * @param orderNo 订单编号
     */
    @Transactional(rollbackFor = Exception.class)
    public void closeWaitTradeOrder(String orderNo) {
        // 关闭订单
        orderInfoDao.lambdaUpdate()
                .eq(OrderInfo::getOrderNo, orderNo)
                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.WAIT.getCode())
                .set(OrderInfo::getOrderStatus, OrderStatusEnum.CLOSE.getCode())
                .update();

        // 关闭交易记录
        orderPayDao.lambdaUpdate()
                .eq(OrderPay::getOrderNo, orderNo)
                .eq(OrderPay::getOrderStatus, OrderStatusEnum.WAIT.getCode())
                .set(OrderPay::getOrderStatus, OrderStatusEnum.CLOSE.getCode())
                .update();
    }

    /**
     * 关闭待支付订单
     *
     * @param orderNos 订单编号
     */
    @Transactional(rollbackFor = Exception.class)
    public void closeWaitTradeOrders(List<String> orderNos) {
        // 关闭订单
        orderInfoDao.lambdaUpdate()
                .in(OrderInfo::getOrderNo, orderNos)
                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.WAIT.getCode())
                .set(OrderInfo::getOrderStatus, OrderStatusEnum.CLOSE.getCode())
                .update();

        // 关闭交易记录
        orderPayDao.lambdaUpdate()
                .in(OrderPay::getOrderNo, orderNos)
                .eq(OrderPay::getOrderStatus, OrderStatusEnum.WAIT.getCode())
                .set(OrderPay::getOrderStatus, OrderStatusEnum.CLOSE.getCode())
                .update();
    }

    private void handleOrderSuccessBiz(Long userId, Integer goodsType, Long goodsId, String orderNo) {
        if (GoodsTypeEnum.EXAM.getCode().equals(goodsType)) {
            orderSuccessExamHandle(goodsId, userId, ExamRegistrationWayEnum.AUTO_REGISTER.getCode(), orderNo);
        }
    }

    public void orderSuccessExamHandle(Long examId, Long userId, Integer registrationWay, String orderNo) {
        // 判断课程是否存在
        Exam exam = examDao.getById(examId);
        if (ObjectUtil.isNull(exam)) {
            throw new BaseException("考试：" + exam.getId() + "不存在");
        }

        // 判断是否进行绑定
        ExamUser examUser = examUserDao.getByExamTypeAndExamIdAndUserId(exam.getExamType(), exam.getId(), userId);
        if (ObjectUtil.isNotNull(examUser)) {
            // 对其进行更新
            ExamUser updateExamUser = new ExamUser();
            updateExamUser.setId(examUser.getId());
            if (ValidityDateTypeEnum.LONG_EFFECTIVE.getCode().equals(exam.getValidityDateType())) {
                // 长期有效
                updateExamUser.setLongEffective(Boolean.TRUE);
            } else {
                // 非长期有效
                updateExamUser.setLongEffective(Boolean.FALSE);
                if (ValidityDateTypeEnum.FIXED_PERIOD.getCode().equals(exam.getValidityDateType())) {
                    updateExamUser.setBeginTime(exam.getBeginTime());
                    updateExamUser.setEndTime(exam.getEndTime());
                } else if (ValidityDateTypeEnum.FIXED_DAY.getCode().equals(exam.getValidityDateType())) {
                    // 如果是绑定中的状态
                    if (examUser.getEndTime().isAfter(LocalDateTime.now())) {
                        // 结束时间在当前时间之后则直接进行时间累加
                        updateExamUser.setEndTime(examUser.getEndTime().plusDays(exam.getDays()));
                    } else {
                        // 否则进行重新计算
                        updateExamUser.setBeginTime(LocalDateTime.now());
                        updateExamUser.setEndTime(examUser.getBeginTime().plusDays(exam.getDays()));
                    }
                }
            }
            updateExamUser.setPayment(Boolean.TRUE);
            updateExamUser.setRegistrationWay(registrationWay);
            updateExamUser.setRegistrationTime(LocalDateTime.now());
            updateExamUser.setOrderNo(StrUtil.isNotBlank(orderNo) ? orderNo : "");
            examUserDao.updateById(updateExamUser);
        } else {
            // 不存在，创建新的关联记录
            ExamUser saveExamUser = new ExamUser();
            saveExamUser.setUserId(userId);
            saveExamUser.setExamId(exam.getId());
            saveExamUser.setExamType(exam.getExamType());
            if (ValidityDateTypeEnum.LONG_EFFECTIVE.getCode().equals(exam.getValidityDateType())) {
                saveExamUser.setLongEffective(Boolean.TRUE);
            } else {
                saveExamUser.setLongEffective(Boolean.FALSE);
                if (ValidityDateTypeEnum.FIXED_PERIOD.getCode().equals(exam.getValidityDateType())) {
                    saveExamUser.setBeginTime(exam.getBeginTime());
                    saveExamUser.setEndTime(exam.getEndTime());
                } else if (ValidityDateTypeEnum.FIXED_DAY.getCode().equals(exam.getValidityDateType())) {
                    saveExamUser.setBeginTime(LocalDateTime.now());
                    saveExamUser.setEndTime(saveExamUser.getBeginTime().plusDays(exam.getDays()));
                }
            }

            saveExamUser.setPayment(Boolean.TRUE);
            saveExamUser.setRegistrationWay(registrationWay);
            saveExamUser.setRegistrationTime(LocalDateTime.now());
            saveExamUser.setOrderNo(StrUtil.isNotBlank(orderNo) ? orderNo : "");
            examUserDao.save(saveExamUser);
        }

        // 统计学习人数、购买数
        LambdaQueryWrapper<ExamUser> wrapper = new LambdaQueryWrapper<ExamUser>()
                .eq(ExamUser::getExamType, exam.getExamType())
                .eq(ExamUser::getExamId, exam.getId());
        int count = (int) examUserDao.count(wrapper);

        wrapper.eq(ExamUser::getPayment, Boolean.TRUE);
        int countBuy = (int) examUserDao.count(wrapper);
        examDao.lambdaUpdate()
                .eq(Exam::getId, exam.getId())
                .set(Exam::getCountStudy, count)
                .set(Exam::getCountBuy, countBuy)
                .update();
    }
}

